package com.wdb.common.configurer.resolver;

import com.netflix.hystrix.exception.HystrixBadRequestException;
import com.wdb.common.ex.BusinessException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.wdb.common.constant.Constant.Error.*;

/**
 * @author shiyu.long
 */
@RestControllerAdvice
public class ExceptionResolverConfigurer {
    private final Logger logger = LoggerFactory.getLogger(ExceptionResolverConfigurer.class);

    public ExceptionResolverConfigurer() {
        if (logger.isDebugEnabled()) {
            logger.info("{}", "added exception resolver");
        }
    }

    @Resource
    private MessageSource messageSource;

    private Map<String, Object> fill(String message) {
        Map<String, Object> map = new HashMap<>();
        if (StringUtils.isEmpty(message)) {
            return map;
        }
        map.put(ERROR_KEY, message);
        return map;
    }

    private Map<String, Object> fillFieldError(BindingResult result) {
        if (ObjectUtils.isEmpty(result)) {
            return fill(null);
        }
        List<FieldError> fieldErrors = result.getFieldErrors();
        if (!ObjectUtils.isEmpty(fieldErrors)) {
            StringBuilder builder = new StringBuilder();
            for (FieldError fieldError : fieldErrors) {
                builder.append(fieldError.getField()).append(fieldError.getDefaultMessage()).append(",");
            }
            String message = builder.substring(0, builder.length() - 1);
            if (logger.isDebugEnabled()) {
                logger.error(message);
            }
            return fill(message);
        }
        return fill(null);
    }

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = MissingServletRequestParameterException.class)
    public Map<String, Object> handleMissingServletRequestParameterException(MissingServletRequestParameterException e) {
        return fill(messageSource.getMessage(ERROR_MISSING_PARAMETER, new Object[]{e.getParameterName()}, LocaleContextHolder.getLocale()));
    }

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = BindException.class)
    public Map<String, Object> handleBindException(BindException e) {
        return fillFieldError(e.getBindingResult());
    }

    @ResponseStatus(value = HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Map<String, Object> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        return fillFieldError(e.getBindingResult());
    }

    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    @ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
    public Map<String, Object> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
        if (logger.isDebugEnabled()) {
            logger.error(e.getMessage());
        }
        return fill(messageSource.getMessage(ERROR_METHOD_NOT_ALLOWED, new Object[]{e.getMethod()}, LocaleContextHolder.getLocale()));
    }

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = BusinessException.class)
    public Map<String, Object> handleBusinessException(BusinessException e) {
        if (logger.isDebugEnabled()) {
            logger.error("{}", e.getMessage());
        }
        String message = e.getMessage();
        if (StringUtils.isEmpty(message)) {
            return fill(messageSource.getMessage(ERROR_BUSINESS, null, LocaleContextHolder.getLocale()));
        }
        return fill(messageSource.getMessage(e.getMessage(), null, LocaleContextHolder.getLocale()));
    }

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = HystrixBadRequestException.class)
    public Map<String, Object> handleHystrixBadRequestException(HystrixBadRequestException e) {
        if (logger.isDebugEnabled()) {
            logger.error("{}", e.getMessage());
        }
        String message = e.getMessage();
        if (StringUtils.isEmpty(message)) {
            return fill(messageSource.getMessage(ERROR_BUSINESS, null, LocaleContextHolder.getLocale()));
        }
        return fill(messageSource.getMessage(e.getMessage(), null, LocaleContextHolder.getLocale()));
    }

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = HttpMessageNotReadableException.class)
    public Map<String, Object> handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
        if (logger.isDebugEnabled()) {
            logger.error("{}", e.getMessage());
        }

        return fill(messageSource.getMessage(ERROR_JSON, null, LocaleContextHolder.getLocale()));
    }

    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(value = Exception.class)
    public Map<String, Object> handleException(Exception e) {
        e.printStackTrace();
        if (logger.isDebugEnabled()) {
            logger.error("{}", e.getClass().getName());
            logger.error("{}", e.getMessage());
        }
        return fill(messageSource.getMessage(ERROR_SERVER, null, LocaleContextHolder.getLocale()));
    }
}
